package org.byqh.dqeai.config.easyquery;

import com.alibaba.druid.pool.DruidDataSource;
import com.easy.query.api.proxy.client.DefaultEasyEntityQuery;
import com.easy.query.api.proxy.client.DefaultEasyProxyQuery;
import com.easy.query.api.proxy.client.EasyEntityQuery;
import com.easy.query.api.proxy.client.EasyProxyQuery;
import com.easy.query.core.api.client.EasyQueryClient;
import com.easy.query.core.basic.entity.EntityMappingRule;
import com.easy.query.core.basic.entity.TryColumnAndPropertyEntityMappingRule;
import com.easy.query.core.basic.jdbc.conn.ConnectionManager;
import com.easy.query.core.bootstrapper.DatabaseConfiguration;
import com.easy.query.core.bootstrapper.DefaultDatabaseConfiguration;
import com.easy.query.core.bootstrapper.EasyQueryBootstrapper;
import com.easy.query.core.configuration.bean.PropertyDescriptorMatcher;
import com.easy.query.core.configuration.bean.entity.EntityPropertyDescriptorMatcher;
import com.easy.query.core.configuration.column2mapkey.Column2MapKeyConversion;
import com.easy.query.core.configuration.column2mapkey.LowerUnderlinedColumn2MapKeyConversion;
import com.easy.query.core.configuration.nameconversion.NameConversion;
import com.easy.query.core.configuration.nameconversion.impl.UnderlinedNameConversion;
import com.easy.query.core.datasource.DataSourceUnitFactory;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.util.Map;
import java.util.function.Supplier;

/**
 * 对每一个数据源, 定义一个对应EasyQuery
 */
@Component
public class EasyQueryPostProcessor implements BeanDefinitionRegistryPostProcessor, PriorityOrdered {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        if (registry instanceof ListableBeanFactory) {
            ListableBeanFactory factory = (ListableBeanFactory) registry;

            // 获取所有DataSource类型的Bean名称
            String[] dataSourceBeanNames = factory.getBeanNamesForType(DataSource.class);

            for (String dataSourceBeanName : dataSourceBeanNames) {
                BeanDefinition bd = registry.getBeanDefinition(dataSourceBeanName);

                registerEasyQueryClient(factory, dataSourceBeanName, bd.isPrimary());
                registerEasyEntityQuery(factory, dataSourceBeanName, bd.isPrimary());
                registerEasyProxyQuery(factory, dataSourceBeanName, bd.isPrimary());
            }
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) throws BeansException {
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }

    private static <T> String getBeanName(Class<T> clazz, String database) {
        return clazz.getSimpleName() + "_" + database;
    }

    private static void registerEasyQueryClient(ListableBeanFactory factory, String dataSourceBeanName, Boolean isPrimary) {
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) factory;

        Supplier<EasyQueryClient> supplier = () -> {
            DataSource dataSource = factory.getBean(dataSourceBeanName, DataSource.class);

            return buildEasyQueryClient(dataSource);
        };

        BeanDefinitionBuilder builder = BeanDefinitionBuilder
                .genericBeanDefinition(EasyQueryClient.class, supplier)
                .setScope("singleton") // 可选：设置作用域
                .setLazyInit(false)
                .setPrimary(isPrimary);

        // 生成唯一的Bean名称
        String beanName = getBeanName(EasyQueryClient.class, dataSourceBeanName);

        // 注册Bean定义到容器
        registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
    }

    private static void registerEasyEntityQuery(ListableBeanFactory factory, String dataSourceBeanName, Boolean isPrimary) {
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) factory;

        Supplier<EasyEntityQuery> supplier = () -> {
            EasyQueryClient easyQueryClient = factory.getBean(getBeanName(EasyQueryClient.class, dataSourceBeanName), EasyQueryClient.class);

            return new DefaultEasyEntityQuery(easyQueryClient);
        };

        BeanDefinitionBuilder builder = BeanDefinitionBuilder
                .genericBeanDefinition(EasyEntityQuery.class, supplier)
                .setScope("singleton") // 可选：设置作用域
                .setLazyInit(false)
                .setPrimary(isPrimary);

        // 生成唯一的Bean名称
        String beanName = "EasyEntityQuery_" + dataSourceBeanName;

        // 注册Bean定义到容器
        registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
    }

    private static void registerEasyProxyQuery(ListableBeanFactory factory, String dataSourceBeanName, Boolean isPrimary) {
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) factory;

        Supplier<EasyProxyQuery> supplier = () -> {
            EasyQueryClient easyQueryClient = factory.getBean(getBeanName(EasyQueryClient.class, dataSourceBeanName), EasyQueryClient.class);

            return new DefaultEasyProxyQuery(easyQueryClient);
        };

        BeanDefinitionBuilder builder = BeanDefinitionBuilder
                .genericBeanDefinition(EasyProxyQuery.class, supplier)
                .setScope("singleton") // 可选：设置作用域
                .setLazyInit(false)
                .setPrimary(isPrimary);

        // 生成唯一的Bean名称
        String beanName = "EasyProxyQuery_" + dataSourceBeanName;

        // 注册Bean定义到容器
        registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
    }

    /**
     * 构建EasyQueryClient
     */
    private static EasyQueryClient buildEasyQueryClient(DataSource dataSource) {
        return EasyQueryBootstrapper.defaultBuilderConfiguration()
                .setDefaultDataSource(dataSource)
                .replaceService(DataSourceUnitFactory.class, SpringDataSourceUnitFactory.class)
                .replaceService(NameConversion.class, UnderlinedNameConversion.class)
                .replaceService(ConnectionManager.class, SpringConnectionManager.class)
                .useDatabaseConfigure(getDatabaseConfiguration(dataSource))
                .customConfigure(s -> {
                    s.addService(NameConversion.class, UnderlinedNameConversion.class);
                    s.addService(Column2MapKeyConversion.class, LowerUnderlinedColumn2MapKeyConversion.class);
                    s.addService(PropertyDescriptorMatcher.class, EntityPropertyDescriptorMatcher.class);
                    s.addService(EntityMappingRule.class, TryColumnAndPropertyEntityMappingRule.class);
                })
                .optionConfigure(builder -> {
//                    builder.setDeleteThrowError(easyQueryProperties.getDeleteThrow());
//                    builder.setInsertStrategy(easyQueryProperties.getInsertStrategy());
//                    builder.setUpdateStrategy(easyQueryProperties.getUpdateStrategy());
//                    builder.setMaxShardingQueryLimit(easyQueryProperties.getMaxShardingQueryLimit());
//                    builder.setExecutorMaximumPoolSize(easyQueryProperties.getExecutorMaximumPoolSize());
//                    builder.setExecutorCorePoolSize(easyQueryProperties.getExecutorCorePoolSize());
//                    builder.setThrowIfRouteNotMatch(easyQueryProperties.isThrowIfRouteNotMatch());
//                    builder.setShardingExecuteTimeoutMillis(easyQueryProperties.getShardingExecuteTimeoutMillis());
//                    builder.setQueryLargeColumn(easyQueryProperties.isQueryLargeColumn());
//                    builder.setMaxShardingRouteCount(easyQueryProperties.getMaxShardingRouteCount());
//                    builder.setExecutorQueueSize(easyQueryProperties.getExecutorQueueSize());
//                    builder.setDefaultDataSourceName(easyQueryProperties.getDefaultDataSourceName());
//                    builder.setDefaultDataSourceMergePoolSize(easyQueryProperties.getDefaultDataSourceMergePoolSize());
//                    builder.setMultiConnWaitTimeoutMillis(easyQueryProperties.getMultiConnWaitTimeoutMillis());
//                    builder.setWarningBusy(easyQueryProperties.isWarningBusy());
//                    builder.setInsertBatchThreshold(easyQueryProperties.getInsertBatchThreshold());
//                    builder.setUpdateBatchThreshold(easyQueryProperties.getUpdateBatchThreshold());
//                    builder.setPrintSql(easyQueryProperties.isPrintSql());
//                    builder.setStartTimeJob(easyQueryProperties.isStartTimeJob());
//                    builder.setDefaultTrack(easyQueryProperties.isDefaultTrack());
//                    builder.setRelationGroupSize(easyQueryProperties.getRelationGroupSize());
//                    builder.setKeepNativeStyle(easyQueryProperties.isKeepNativeStyle());
//                    builder.setNoVersionError(easyQueryProperties.isNoVersionError());
//                    builder.setReverseOffsetThreshold(easyQueryProperties.getReverseOffsetThreshold());
                })
                .build();
    }

    private static DatabaseConfiguration getDatabaseConfiguration(DataSource dataSource) {
        final Map<String, String> mapDriver = Map.of(
                "oracle.jdbc.OracleDriver",
                "com.easy.query.oracle.config.OracleDatabaseConfiguration",
                "com.kingbase8.Driver",
                "com.easy.query.kingbase.es.config.KingbaseESDatabaseConfiguration",
                "com.mysql.cj.jdbc.Driver",
                "com.easy.query.mysql.config.MySQLDatabaseConfiguration"
        );

        String driverClass = null;
        DatabaseConfiguration configuration = null;

        if (dataSource instanceof HikariDataSource hikariDataSource) {
            driverClass = hikariDataSource.getDriverClassName();
        } else if (dataSource instanceof DruidDataSource druidDataSource) {
            driverClass = druidDataSource.getDriverClassName();
        }

        if (driverClass != null) {
            if (mapDriver.containsKey(driverClass)) {
                configuration = createInstance(mapDriver.get(driverClass), DatabaseConfiguration.class);
            }
        }

        if (configuration == null) {
            configuration = new DefaultDatabaseConfiguration();
        }

        return configuration;
    }

    public static <T> T createInstance(String className, Class<T> type) {
        try {
            Class<?> clazz = Class.forName(className);
            return type.cast(clazz.getDeclaredConstructor().newInstance());
        } catch (Exception e) {
            return null;
        }
    }
}
